package org.springblade.core.launch;

import org.springblade.core.launch.constant.AppConstant;
import org.springblade.core.launch.service.LauncherService;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.*;
import org.springframework.core.io.support.ResourcePropertySource;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 项目启动器，搞定环境变量问题
 *
 * @author Chill
 */
@Configuration
@PropertySource(value = {"classpath:application.yml"})
public class BladeApplication {

	/**
	 * Create an application context
	 * java -jar app.jar --spring.profiles.active=prod --server.port=2333
	 *
	 * @param appName application name
	 * @param source  The sources
	 * @return an application context created from the current state
	 */
	public static ConfigurableApplicationContext run(String appName, Class source, String... args) {
		SpringApplicationBuilder builder = createSpringApplicationBuilder(appName, source, args);
		return builder.run(args);
	}

	public static SpringApplicationBuilder createSpringApplicationBuilder(String appName, Class source, String... args) {
		Assert.hasText(appName, "[appName]服务名不能为空");
		// 读取环境变量，使用spring boot的规则
		ConfigurableEnvironment environment = new StandardEnvironment();
		MutablePropertySources propertySources = environment.getPropertySources();
		propertySources.addFirst(new SimpleCommandLinePropertySource(args));
		propertySources.addLast(new MapPropertySource(StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, environment.getSystemProperties()));
		propertySources.addLast(new SystemEnvironmentPropertySource(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, environment.getSystemEnvironment()));
		try {
			propertySources.addLast(new ResourcePropertySource(AppConstant.APPLICATION_YML));
		}catch (IOException e){
			System.out.println("加载配置文件失败："+ e);
		}
		// 获取配置的环境变量
		String[] activeProfiles = environment.getActiveProfiles();
		// 判断环境:dev、test、prod
		List<String> profiles = Arrays.asList(activeProfiles);
		//配置环境优先使用命令指定环境，未使用命令指定环境则使用配置文件指定的配置环境
		profiles = new ArrayList(profiles);
		if(profiles.size() == 0){
			org.springframework.core.env.PropertySource<?> userDir = environment.getPropertySources().get("class path resource [application.yml]");
			String envstr = userDir.getSource().toString();
			Map<String, String> map = mapStringToMap(envstr);
			String active = map.get(AppConstant.ACTIVE);
			System.out.println("加载配置文件环境："+ active);
			profiles.add(active);
		}
		// 预设的环境
		List<String> presetProfiles = new ArrayList<>(Arrays.asList(AppConstant.DEV_CODE, AppConstant.TEST_CODE, AppConstant.PROD_CODE));
		// 交集
		presetProfiles.retainAll(profiles);
		// 当前使用
		List<String> activeProfileList = new ArrayList<>(profiles);
		Function<Object[], String> joinFun = StringUtils::arrayToCommaDelimitedString;
		SpringApplicationBuilder builder = new SpringApplicationBuilder(source);
		String profile;
		if (activeProfileList.isEmpty()) {
			// 默认dev开发
			profile = AppConstant.DEV_CODE;
			activeProfileList.add(profile);
			builder.profiles(profile);
		} else if (activeProfileList.size() == 1) {
			profile = activeProfileList.get(0);
		} else {
			// 同时存在dev、test、prod环境时
			throw new RuntimeException("同时存在环境变量:[" + StringUtils.arrayToCommaDelimitedString(activeProfiles) + "]");
		}

		String startJarPath = BladeApplication.class.getResource("/").getPath().split("!")[0];
		String activePros = (String)joinFun.apply(activeProfileList.toArray());
		System.out.println(String.format("----启动中，读取到的环境变量:[%s]，jar地址:[%s]----", activePros, startJarPath));
		Properties props = System.getProperties();
		props.setProperty("spring.application.name", appName);
		props.setProperty("spring.profiles.active", profile);
		props.setProperty("info.version", "3.3.1");
		props.setProperty("info.desc", appName);
		props.setProperty("blade.env", profile);
		props.setProperty("blade.name", appName);
		props.setProperty("blade.is-local", String.valueOf(isLocalDev()));
		props.setProperty("blade.dev-mode", profile.equals("prod") ? "false" : "true");
		props.setProperty("blade.service.version", "3.3.1");
		props.setProperty("spring.main.allow-bean-definition-overriding", "true");
		props.setProperty("spring.cloud.nacos.config.prefix", "blade");
		props.setProperty("spring.cloud.nacos.config.file-extension", "yaml");
		props.setProperty("spring.cloud.sentinel.transport.dashboard", "127.0.0.1:8858");
		props.setProperty("spring.cloud.alibaba.seata.tx-service-group", appName.concat("-group"));
		List<LauncherService> launcherList = new ArrayList();
		ServiceLoader.load(LauncherService.class).forEach(launcherList::add);
		launcherList.stream().sorted(Comparator.comparing(LauncherService::getOrder)).collect(Collectors.toList())
				.forEach(launcherService -> launcherService.launcher(builder, appName, profile));
		return builder;
	}

	/**
	 * 判断是否为本地开发环境
	 *
	 * @return boolean
	 */
	public static boolean isLocalDev() {
		String osName = System.getProperty("os.name");
		return StringUtils.hasText(osName) && !(AppConstant.OS_NAME_LINUX.equals(osName.toUpperCase()));
	}

	public static Map<String,String> mapStringToMap(String str){
		str=str.substring(1, str.length()-1);
		String[] strs=str.split(",");
		Map<String,String> map = new HashMap<String, String>();
		for (String string : strs) {
			String key= string.split("=")[0];
			String value = "";
			if( string.split("=").length>1){
				value = string.split("=")[1];
			}
			map.put(key, value);
		}
		return map;
	}

}
